package com.wiwf.model.magicapi;

import com.alibaba.fastjson.JSONObject;
import com.wiwf.model.mapper.admin.bean.SysApiLogEntity;
import com.wiwf.model.mapper.admin.bean.SysMenuEntity;
import com.wiwf.model.bean.SysUserVoto;
import com.wiwf.model.mapper.admin.dao.SysApiLogMapper;
import com.wiwf.model.security.service.SysTokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.ssssssss.magicapi.core.context.RequestEntity;
import org.ssssssss.magicapi.core.interceptor.RequestInterceptor;
import org.ssssssss.magicapi.core.model.ApiInfo;
import org.ssssssss.magicapi.core.model.JsonBean;
import org.ssssssss.magicapi.core.model.Options;
import org.ssssssss.magicapi.core.service.MagicResourceService;
import org.ssssssss.magicapi.core.servlet.MagicCookie;
import org.ssssssss.magicapi.core.servlet.MagicHttpServletRequest;
import org.ssssssss.magicapi.core.servlet.MagicHttpServletResponse;
import org.ssssssss.script.MagicScriptContext;

import java.util.Date;
import java.util.List;

import javax.servlet.http.Cookie;

/**
 * Magic Api 接口拦截配置
 * @author kehui
 */
@Component
public class MagicReqInterceptor implements RequestInterceptor{
    private static final Logger logger = LoggerFactory.getLogger(MagicReqInterceptor.class);

    @Autowired
    private SysTokenService tokenService;
    @Autowired
    MagicResourceService magicResourceService;
    @Autowired
    SysApiLogMapper apiLogMapper;

    /**
     * 请求之前执行
     *
     * @param requestEntity 请求对象
     * @return 当返回对象时，直接将此对象返回到页面，返回null时，继续执行后续操作
     */
    @Override
    public Object preHandle(RequestEntity requestEntity) {
        return preHandle(requestEntity.getApiInfo(), requestEntity.getMagicScriptContext(), requestEntity.getRequest(), requestEntity.getResponse());
    }

    /**
     * 请求之前执行
     *
     * @param info     接口信息
     * @param context  脚本上下文
     * @param mgRequest  HttpServletRequest
     * @param mgResponse HttpServletResponse
     * @return 当返回对象时，直接将此对象返回到页面，返回null时，继续执行后续操作
     */
    @Override
    public Object preHandle(ApiInfo info, MagicScriptContext context, MagicHttpServletRequest mgRequest, MagicHttpServletResponse mgResponse) {
        // -- 接口被直接调用时 获取 token 的方式
        String token = mgRequest.getHeader("Magic-Token");
        if(ObjectUtils.isEmpty(token)) {
            token = getToken(mgRequest);
        }

        // -- 需要登录接口
        String IS_TRUE = "true";
        if (IS_TRUE.equals(info.getOptionValue(Options.REQUIRE_LOGIN))) {
            if (! ObjectUtils.isEmpty(token)) {
                if (ObjectUtils.isEmpty(tokenService.getTokenUser(token))) {
                    return new JsonBean<>(401, "用户未登录");
                }
            } else {
                return new JsonBean<>(401, "用户未登录");
            }
        }

        // -- 需要权限接口
        String role = info.getOptionValue(Options.ROLE);
        String permission = info.getOptionValue(Options.PERMISSION);
        if (ObjectUtils.isEmpty(role) && ObjectUtils.isEmpty(permission)) {
            return null;
        } else {
            if (ObjectUtils.isEmpty(token)) {
                return new JsonBean<>(401, "用户未登录");
            }

            SysUserVoto userVoto = tokenService.getTokenUser(token);
            if(ObjectUtils.isEmpty(userVoto)){
                return new JsonBean<>(401, "用户未登录");
            }
            String roleCode = userVoto.getRoleCode();

            if (!ObjectUtils.isEmpty(permission) && !ObjectUtils.isEmpty(role)) {
                if (role.equalsIgnoreCase(roleCode)) {
                    List<SysMenuEntity> menus = userVoto.getMenus();
                    for (SysMenuEntity menuEntity : menus) {
                        String menuCode = menuEntity.getMenuCode();
                        if(permission.equalsIgnoreCase(menuCode)) {
                            return null;
                        }
                    }
                }
            } else if (!ObjectUtils.isEmpty(permission)) {
                List<SysMenuEntity> menus = userVoto.getMenus();
                for (SysMenuEntity menuEntity : menus) {
                    String menuCode = menuEntity.getMenuCode();
                    if(permission.equalsIgnoreCase(menuCode)) {
                        return null;
                    }
                }
            } else if (!ObjectUtils.isEmpty(role)) {
                if (role.equalsIgnoreCase(roleCode)) {
                    return null;
                }
            }
        }

        return new JsonBean<>(403, "用户权限不足");
    }

    /**
     * 执行完毕之后执行
     *
     * @param info        接口信息
     * @param context     脚本上下文
     * @param returnValue 即将要返回到页面的值
     * @param request     HttpServletRequest
     * @param response    HttpServletResponse
     * @return 返回到页面的对象, 当返回null时执行后续拦截器，否则直接返回该值，不执行后续拦截器
     */
    @Override
    public Object postHandle(ApiInfo info, MagicScriptContext context, Object returnValue, MagicHttpServletRequest request, MagicHttpServletResponse response) {
        return null;
    }

    /**
     * 执行完毕之后执行
     *
     * @param requestEntity 请求对象
     * @param returnValue   即将要返回到页面的值
     * @return 返回到页面的对象, 当返回null时执行后续拦截器，否则直接返回该值，不执行后续拦截器
     */
    @Override
    public Object postHandle(RequestEntity requestEntity, Object returnValue) {
        MagicHttpServletRequest request = requestEntity.getRequest();
        MagicHttpServletResponse response = requestEntity.getResponse();
        ApiInfo info = requestEntity.getApiInfo();

        String apiName = info.getName();
        String apiPath = request.getRequestURI();


        if(apiPath.endsWith("/img") || apiPath.endsWith(".img") || apiPath.endsWith(".png") || apiPath.endsWith(".svg")){
            return null;
        } else if(apiPath.startsWith("/api/test/") || apiPath.startsWith("/layui/") || apiPath.startsWith("/api/page/")){
            return null;
        }
        if(apiPath.startsWith("/api/system/") &&
                (apiPath.endsWith("/list") || apiPath.endsWith("/tree") || apiPath.endsWith("/roles") || apiPath.endsWith("/users"))){
            return null;
        }

        String apiMethod = request.getMethod();
        String reqBody = "";
        String resBody = "";
        try {
            reqBody = JSONObject.toJSONString(requestEntity.getParameters());
            if(requestEntity.getParameters().isEmpty()){
                reqBody = JSONObject.toJSONString(requestEntity.getRequestBody());
            }
            resBody = JSONObject.toJSONString(returnValue);
        } catch (Exception e){

        }

        Long costTime = System.currentTimeMillis() - requestEntity.getRequestTime();
        Date createDate = new Date(requestEntity.getRequestTime());
        String userAgent = request.getHeader("User-Agent");
        String userIp = request.getRemoteAddr();

        String userToken = getToken(request);
        String userName = "未登录";
        if(ObjectUtils.isEmpty(userToken)){
            userToken = "";
        } else {
            SysUserVoto user = tokenService.getTokenUser(userToken);
            if(! ObjectUtils.isEmpty(user)){
                userName = tokenService.getTokenUser(userToken).getName();
            }
        }
        apiLogMapper.save(new SysApiLogEntity(apiName, apiPath, apiMethod, costTime, createDate, userAgent, userIp, userToken, userName, reqBody, resBody));
        // logger.info("\n\n token: {}, ip: {}, api : {} \n\n", userToken, userIp, apiName);

        return null;
    }

    /**
     * 接口执行完毕之后执行
     *
     * @param requestEntity 请求对象
     * @param returnValue   即将要返回到页面的值
     * @param throwable     异常对象
     */
    @Override
    public void afterCompletion(RequestEntity requestEntity, Object returnValue, Throwable throwable) {
        afterCompletion(requestEntity.getApiInfo(), requestEntity.getMagicScriptContext(), returnValue, requestEntity.getRequest(), requestEntity.getResponse(), throwable);
    }

    /**
     * 接口执行完毕之后执行
     *
     * @param info        接口信息
     * @param context     脚本上下文
     * @param returnValue 即将要返回到页面的值
     * @param request     HttpServletRequest
     * @param response    HttpServletResponse
     * @param throwable   异常对象
     */
    @Override
    public void afterCompletion(ApiInfo info, MagicScriptContext context, Object returnValue, MagicHttpServletRequest request, MagicHttpServletResponse response, Throwable throwable) {

    }
    
    private String getToken(MagicHttpServletRequest request) {
    	String tokenKey = tokenService.getTokenKey();
		try {
			MagicCookie[] cookies = request.getCookies();
			
			if(cookies == null || cookies.length == 0){
				return request.getHeader(tokenKey);
			}
			for (MagicCookie cookie : cookies) {
				if(cookie.getName().equals(tokenKey)) {
					return cookie.getValue();
				}
			}
		} catch (Exception e) {
			return null;
		}
		return request.getHeader(tokenKey);
	}

}
